﻿using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;
using PIT.Labs.ProjectRepository.Owl2DotNet;
using PIT.Labs.ProjectRepository.Owl2DotNet.Rdf;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using System.Xml;

namespace PIT.Labs.ProjectRepository.OntologyFeature.TaxonomyCreator
{
    /// <summary>
    /// User control to automatically creates (1) a taxonomy from the classes 
    /// of an ontology and (2) a list with fields from properties of the 
    /// ontology
    /// </summary>
    public partial class TaxonomyCreatorUserControl : UserControl
    {
        private readonly SPSite spSite = SPContext.Current.Site;

        private HashSet<string> processedClasses;

        private HashSet<string> selectedObjectProperties = new HashSet<string>();
        private HashSet<string> selectedDataProperties = new HashSet<string>();

        /// <summary>
        /// OnLoad event handler. Makes sure the page state is transferred
        /// between multiple calls of the page and does setup of page elements
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            EnableViewState = true;

            CreateOntologyRadioButtons();           
        }

        private void CreateOntologyRadioButtons()
        {
            if (OntologyRadioButtonList.Items.Count > 0) return;

            SPWeb spWeb = spSite.OpenWeb();
            SPFolder spLibrary = spWeb.Folders["OntologyXMLFiles"];

            foreach (SPFile file in spLibrary.Files)
            {
                OntologyRadioButtonList.Items.Add(file.Name);
            }
        }

        private TermSet CreateTaxonomyFromClasses(IOntology ontology, Guid ontologyGuid)
        {
            TaxonomySession tSession = new TaxonomySession(spSite);

            TermStore tStore = tSession.TermStores[0];

            if (tStore.Groups.Count(x => x.Name == ontology.Label) == 0)
            {
                tStore.CreateGroup(ontology.Label);
            }
            else
            {
                // Deleting causes currently tagged data to produce errors or get lost
                //foreach(var ts in tStore.Groups[ontology.Label].TermSets){
                //    ts.Delete();
                //}
                // if there already is a term set do not do anything.
                return tStore.Groups[ontology.Label].TermSets["Terms"];
            }

            var tSet = tStore.Groups[ontology.Label].CreateTermSet("Terms");

            processedClasses = new HashSet<string>();

            AddChildrenToTerm(tSet, ontology.Thing, ontologyGuid);

            tStore.CommitAll();

            return tSet;
        }

        private void AddChildrenToTerm(TermSetItem t, IClass c, Guid ontologyGuid)
        {
            if(!processedClasses.Contains(c.Uri.OriginalString)){
                processedClasses.Add(c.Uri.OriginalString);
                foreach (IClass childClass in c.SubClasses)
                {
                    Term childTerm = t.CreateTerm(childClass.Label, CultureInfo.CurrentCulture.LCID);
                    childTerm.SetCustomProperty("ontology::uri", childClass.Uri.OriginalString);
                    childTerm.SetCustomProperty("ontology::guid", ontologyGuid.ToString());
                    AddChildrenToTerm(childTerm, childClass, ontologyGuid);
                }
            }
        }

        /// <summary>
        /// Button1 onClick event handler. Creates the taxonomy based 
        /// on the currently selected ontology
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Button1_Click(object sender, EventArgs e)
        {
            if (OntologyRadioButtonList.SelectedIndex != -1)
            {
                SPWeb spWeb = spSite.OpenWeb();
                SPFolder spLibrary = spWeb.Folders["OntologyXMLFiles"];

                RdfXmlParser parser = new RdfXmlParser();
                
                Stream fileReader = spLibrary.Files[OntologyRadioButtonList.SelectedIndex].OpenBinaryStream();

                CreateTaxonomyFromClasses(OntologyHelper.GetOntology(spLibrary.Files[OntologyRadioButtonList.SelectedIndex].UniqueId), spLibrary.Files[OntologyRadioButtonList.SelectedIndex].UniqueId);
            }
        }

        /// <summary>
        /// Button3 onClick event handler. Selects the current ontology to 
        /// display full details.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Button3_Click(object sender, EventArgs e)
        {
            if (OntologyRadioButtonList.SelectedIndex != -1)
            {
                SPWeb spWeb = spSite.OpenWeb();
                SPFolder spLibrary = spWeb.Folders["OntologyXMLFiles"];
                
                OntologyName.Text = spLibrary.Files[OntologyRadioButtonList.SelectedIndex].Name;

                ListProptertiesFromOntology(OntologyHelper.GetOntology(spLibrary.Files[OntologyRadioButtonList.SelectedIndex].UniqueId));

                OntologyId.Value = OntologyRadioButtonList.SelectedIndex.ToString(CultureInfo.InvariantCulture);
            }
        }

        private void ListProptertiesFromOntology(IOntology ontology)
        {
            DataPropertyCheckBoxList.Items.Clear();
            ObjectPropertyCheckBoxList.Items.Clear();

            foreach(var prop in ontology.DataProperties){
                var item = new ListItem(prop.Label, prop.Uri.OriginalString);
                item.Selected = true;
                DataPropertyCheckBoxList.Items.Add(item);
            }

            foreach (var prop in ontology.ObjectProperties){
                var item = new ListItem(prop.Label, prop.Uri.OriginalString);
                item.Selected = true;
                ObjectPropertyCheckBoxList.Items.Add(item);
            }

            TextBox1.Text = ontology.Label;
        }

        /// <summary>
        /// Button2 onClick event handler. Creates a list based 
        /// on the currently selected properties. Also creates a view 
        /// displaying the fields from the ontology
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Button2_Click(object sender, EventArgs e)
        {
            SPWeb spWeb = spSite.OpenWeb();
            SPFolder spLibrary = spWeb.Folders["OntologyXMLFiles"];

            IOntology ontology = OntologyHelper.GetOntology(spLibrary.Files[int.Parse(OntologyId.Value, CultureInfo.InvariantCulture)].UniqueId);

            CreateLibraryViewFromProperties(ontology, spLibrary.Files[int.Parse(OntologyId.Value, CultureInfo.InvariantCulture)].UniqueId);
        }

        private void CreateLibraryViewFromProperties(IOntology ontology, Guid ontologyGuid)
        {
            // TODO: Refactor method
            SPWeb spWeb = spSite.OpenWeb();

            SPList spLibrary;
            spLibrary = spWeb.Lists.TryGetList(TextBox1.Text);
            if (spLibrary == null)
            {
                Guid listGuid = spWeb.Lists.Add(TextBox1.Text, "Document library based on ontology: " + ontology.Label, SPListTemplateType.DocumentLibrary);
                spLibrary = spWeb.Lists[listGuid];
            }

            foreach(SPView v in spLibrary.Views){
                if (v.Title == "ontology::" + ontology.Label)
                {
                    spLibrary.Views.Delete(spLibrary.Views["ontology::" + ontology.Label].ID);
                    break;
                }
            }            
            
            spLibrary.RootFolder.Properties["ontology::fileUri"] = ontologyGuid.ToString();

            var fieldCollection = spLibrary.DefaultView.ViewFields.ToStringCollection();

            var checkedDataProperties = from x in DataPropertyCheckBoxList.Items.OfType<ListItem>()
                                        where x.Selected == true
                                        select x.Value;

            foreach (var p in checkedDataProperties)
            {
                var dataProp = ontology.GetDataProperty(new Uri(p));

                // --- Cusom field by XML:
                string strFieldasXML = GetCreateFieldAsXml(Guid.NewGuid(), dataProp.Label, false, "OntologyData", "OntologyData");
                var fieldName = spLibrary.Fields.AddFieldAsXml(strFieldasXML);
                var field = (OntologyDataField)spLibrary.Fields[dataProp.Label];

                field.OntologyGuid = ontologyGuid;
                field.PropertyUri = dataProp.Uri;

                spLibrary.DefaultView.ViewFields.Add(field);

                fieldCollection.Add(dataProp.Label);

                field.Required = false;
                field.Update();
            }

            var checkedObjectProperties = from x in ObjectPropertyCheckBoxList.Items.OfType<ListItem>()
                                          where x.Selected == true
                                          select x.Value;

            foreach (var p in checkedObjectProperties)
            {
                var objProp = ontology.GetObjectProperty(new Uri(p));

                // --- Custom field by Typename
                //var field = new OntologyLookupField(spLibrary.Fields, objProp.Label);
                //var field = (OntologyLookupField)spLibrary.Fields.CreateNewField("OntologyLookup", objProp.Label);
                //field.Contents = objProp.Range.First().Members;
                
                // --- Default Lookup field
                //string fieldName = spLibrary.Fields.AddLookup(valueProp.Label, spLibrary.ID, true);
                //var field = (SPFieldLookup)spLibrary.Fields.GetFieldByInternalName(fieldName);
                //field.LookupField = spLibrary.Fields["Name"].InternalName;

                // --- Cusom field by XML:
                string strFieldasXML = GetCreateFieldAsXml(Guid.NewGuid(), objProp.Label, false, "OntologyLookup", "OntologyLookup");
                var fieldName = spLibrary.Fields.AddFieldAsXml(strFieldasXML);
                var field = (OntologyLookupField)spLibrary.Fields[objProp.Label];

                field.OntologyGuid = ontologyGuid;
                field.PropertyUri = objProp.Uri;

                field.Required = false;

                spLibrary.DefaultView.ViewFields.Add(field);
                fieldCollection.Add(objProp.Label);

                field.Update();
            }

            if (CbxAddTypeRelation.Checked)
            {
                var tSet = CreateTaxonomyFromClasses(ontology, ontologyGuid);
                var tf = (TaxonomyField)spLibrary.Fields.CreateNewField("TaxonomyFieldType", "rdf:type");
                tf.SspId = tSet.TermStore.Id;
                tf.Group = tSet.Group.Name;
                tf.TermSetId = tSet.Id;
                tf.AllowMultipleValues = false;
                tf.Title = "rdf:type";

                spLibrary.Fields.Add(tf);
                fieldCollection.Add("rdf:type");
            }

            spLibrary.Views.Add("ontology::" + ontology.Label, fieldCollection, spLibrary.DefaultView.Query, spLibrary.DefaultView.RowLimit, spLibrary.DefaultView.Paged, false);

            spWeb.Update(); 
            spLibrary.Update();
            spLibrary.RootFolder.Update();
            spLibrary.DefaultView.Update();
        }


        internal string GetCreateFieldAsXml(Guid oId, string strDisplayName, bool Required, string strFieldType, string strFieldName)
        {
            XmlElement element = new XmlDocument().CreateElement("Field"); // Creating the “Field” element for the Inner XML schema

            element.SetAttribute("ID", oId.ToString()); // Setting the GUID of the field from value passed

            element.SetAttribute("Type", strFieldType); // Setting the Type name registered in the “Fldtypes*.xml” file

            element.SetAttribute("Name", strFieldName); // Setting the Field name registered in the “Fldtypes*.xml” file

            element.SetAttribute("DisplayName", strDisplayName); // Any unique Display Name 

            element.SetAttribute("Required", Required.ToString(CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture));

            return element.OuterXml; // Returning the OuterXML to create the field as XML


        }
    }
}
